Amazon ECSをHerokuぽくラップするEmpireを試してみた
ども、大瀧です。
先週Docker界隈で話題になった、Empireというプロダクトを試してみたのでレポートします。
Empireとは
Empireは、教育向けメッセージングサービスを手がけるRemind社が開発するオープンソースのDockerコンテナ管理ソフトウェアです。HerokuライクなCLIを備えていて、PaaSっぽくDockerコンテナを管理することができます。
DockerでHerokuのようなPaaSというとDokkuが思い浮かぶところですが、Dokkuには複数ホストをクラスタとして管理する機能が無く、本番環境向けとしては機能が足りません。一方、Dockerコンテナ管理ソフトウェアというとGoogleが開発するKubernetesやCoreOS fleetが有名ですが、どちらも構築および運用に独特なスキルが必要です。Empireはコンテナ管理に必要なコンポーネントをなるべくAWSのマネージドサービスに寄せることで、各サービスの連携とラッピングに徹しており、AWSの知識があれば最小限の学習コストで済ませられるメリットがあります。
Remind社自身、元々HerokuユーザーでfleetやKubernetesを検討した末にEmpireに行き着いた経緯があり、その様子は以下のブログに綴られています。
チュートリアル
動作確認環境
- OS : Mac OS X 10.10.3 Yosemite
- AWS CLI : バージョン 1.7.35
Empireは以下のコンポーネントで構成されます。
サーバーはDockerイメージが用意されているのでDocker環境があれば簡単に実行できますし *1、CLIもGo言語で記述されており、Linux 64bit版とMac OS X版のバイナリがGitHubからダウンロードできます。さらに、Empireサーバーと関連するAWSサービスをまとめてデプロイするCloudFormationテンプレートも提供されています。今回はGitHubにあるドキュメントに従い、CloudFormationベースの手順でEmpireを試してみます。
AWS Marketplaceの承認
CLoudFormationテンプレートからはMarketplaceのAmazon ECS-Optimized Amazon Linux AMIが呼び出されるので、あらかじめ以下のリンクを踏んで[Accept Terms]ボタンをクリック、AMI使用許諾に承認しておきます。
CloudFormationの展開とCLIのセットアップ
続いて、CloudFormationで各コンポーネントをデプロイします。EmpireのGitHubリポジトリに各操作がまとめられたシェルスクリプトがあるので、今回はそちらを利用します。スクリプト内でAWS CLIを実行するため、あらかじめインストールしておきましょう。
$ git clone https://github.com/remind101/empire.git Cloning into 'empire'... remote: Counting objects: 8706, done. remote: Compressing objects: 100% (57/57), done. remote: Total 8706 (delta 23), reused 0 (delta 0), pack-reused 8646 Receiving objects: 100% (8706/8706), 7.13 MiB | 91.00 KiB/s, done. Resolving deltas: 100% (4355/4355), done. Checking connectivity... done. $ cd empire $ ls CHANGELOG.md LICENSE README.md circle.yml docs/ mkdocs.yml CONTRIBUTING.md Makefile bin/ docker-compose.yml empire/ relay/ $ pip install awscli (略) $ aws --version aws-cli/1.7.35 Python/2.7.10 Darwin/14.3.0 $
Marketplace AMIは旧バージョンのAMIに利用制限がかかっているようで、GitHubで公開されているCloudFormationテンプレートをそのまま実行するとエラーになりました。最新バージョンのAMI IDを確認(先ほどのMarketplaceのページにあります)し、テンプレートのAmiIdを変更してからスクリプトを実行します。テンプレートファイル名はdocs/cloudformation.jsonです。
"AmiId" : { "Type": "String", "Description": "AMI Id", "Default": "ami-1e091d76" },
執筆時点では、2015.03.dが最新で、AMI IDはami-bf4db8d4でした。変更したらスクリプトを実行します。スクリプトファイルはbin/bootstrapです。実行時にSSHキーペアとDockerリポジトリの認証情報を聞かれます。今回はDocker HubのPublicイメージを利用するため、[N]と入力します。
$ ./bin/bootstrap AWS SSH KeyName: <Your Key Name> Do you have a docker account & want to use it for private repo access? [y/N] N ==> Launching empire in AZs: us-east-1a us-east-1b, Cloudformation Stack empire-XXXXXX ==> Waiting for stack to complete ==> Status: CREATE_IN_PROGRESS ==> Stack empire-XXXXXXXX complete. Now run the following commands - when asked for a username, enter 'fake'. The password is blank: $ export EMPIRE_API_URL=http://empire-XX-LoadBala-XXXXXXXXXX-XXXXXXXXXXX.us-east-1.elb.amazonaws.com/ $ emp login $
AWS Management ConsoleでCloudFormationの管理画面を見ると、スタック作成が進んでいることが確認できます。CloudFormationによって以下が作成されます。
- VPC
- VPC 1つ
- Publicサブネット 2つ
- IAM
- IAMロール、インスタンスプロファイル : ECS用
- IAMユーザー、APIキー : Empireサーバー用
- EC2
- ECSインスタンスの起動設定、Auto Scalingグループ
- ELB : Empireサーバー用
- ECS
- ECSクラスタ
- タスク定義 : Empireサーバー用
- ECSサービス : Empireサーバー用
- Route 53
- Private Hosted Zone : Empireサービスディスカバリ用.empireゾーン(後述)
さらっと最近サポートされたECSのリソース定義やIAM周りもきちんと含まれていて、しっかり作り込まれている印象です。
並行して、CLIをローカルにインストールしておきます。GitHubのリリースページから対応するOSのアーカイブをダウンロードし、PATHの通っている場所に配置します。今回はホームディレクトリ以下にbin/ディレクトリを作成し、配置しました。empコマンドを実行し、ヘルプが表示されればOKです。
$ mkdir ~/bin $ tar zxf ~/Downloads/emp-macos-amd64.tgz -C ~/bin
: export PATH=$PATH:$HOME/bin
$ emp Usage: emp <command> [-a <app or remote>] [options] [arguments] Commands: create create an app apps list apps ps list processes releases list releases :
続いて、CLIからEmpireサーバーに接続します。bin/bootstrapの実行結果末尾に表示されたコマンドライン(環境変数の設定)を実行しログインします。ログイン時の認証はダミー(ユーザー名:fake/パスワード"なし(Enterキー押下))です。
$ export EMPIRE_API_URL=http://empire-XX-LoadBala-XXXXXXXXXX-XXXXXXXXXXX.us-east-1.elb.amazonaws.com/ $ emp login Enter email [fake]: fake Enter password: Logged in. $
コンテナのデプロイ
それでは、EmpireからDockerコンテナをデプロイしてみましょう。現時点ではコンテナの実行コマンド指定が独特 *2なため、一般的なDockerイメージをそのまま流用することはできません。サンプルのremind101/acme-inc:latestで試してみます。
$ emp deploy remind101/acme-inc:latest
デプロイしたDockerイメージはコンテナ定義としてEmpireではアプリケーションという単位で管理します。Empireアプリケーションの一覧はemp appsで確認します。
$ emp apps acme-inc Jun 21 23:18 $
デプロイされたアプリケーションでは、Dockerイメージに含まれるProcfileファイルのwebプロセスのDockerコンテナが1つ実行されます。実際のアプリケーションプロセスはemp psで確認します。
$ emp ps -a acme-inc v1.web.e6a59d1d-beb6-4b81-8acc-b26da2b6d5ed 1X RUNNING 15m "acme-inc server" $
Dockerコンテナの実行数を増やすためにemp scaleコマンドでスケールアウトさせてみます。
$ emp scale -a acme-inc web=2 Scaled acme-inc to web=2:1X. $ emp ps -a acme-inc v1.web.9930faf5-57c1-474e-bf02-10f421e890fa 1X RUNNING 16h "acme-inc server" v1.web.e6a59d1d-beb6-4b81-8acc-b26da2b6d5ed 1X RUNNING 16h "acme-inc server" $
コンテナ数が増えました!
AWSコンポーネントの確認
EmpireによってDockerコンテナが実行される様子はわかりましたが、実際のAWS環境ではどのようにデプロイされているのでしょうか。
AWS Management ConsoleのECSクラスタのServicesタブを見ると、EmpireサーバーのService(empir-Servi-...)以外に...--webと...--workerの2つのServiceがあることがわかります。これがEmpireによって登録されたEmpireアプリケーションのプロセスに対応します。現時点ではECSタスク定義のコンテナ定義と対応します。
*--webServiceをクリックすると、ECSタスク(≒Dockerコンテナ)が2つ実行されていることがわかります。このタスク名と先程のemp psコマンドの結果(web=2)が対応することがわかりますね。また、webプロセスの場合は自動で内部向けELBが作成、Serviceに追加されるようにもなっています。画面右上のLoad Balancersで確認できますね。
さらに、この内部向けELBのDNS名はCloudFormationで作成されたRoute 53のPrivate Hosted Zoneである.empireゾーンにアプリケーション名でレコードが追加されます。
これにより、VPC内部から<アプリケーション名>.empireでELB経由でDockerコンテナにアクセスできるようになっています。ECSインスタンスにログインしcurlコマンドで試してみます。
$ sudo yum install -y curl : $ curl acme-inc.empire Ok $
アクセスできました!
ただ、これはあくまでEmpire内部でのサービスディスカバリのために用意されたもので、インターネットからのDNS問い合わせ、およびWebアクセスに対応するものではありません。別途、この内部向けエンドポイントにトラフィックを転送するリバースプロキシを実装する必要があります。前述のEricさんのブログでは"docker run remind101/routerで簡単に試せるよ!"と書いてあるのですが、残念ながらそのようなDockerイメージを見つけることができませんでした。。。ので、Nginxで自前で実装してみた例を紹介します。
Empire Router(Nginx)の例
以下の規則で任意のサブドメインを内部向けエンドポイントに転送するリバースプロキシをNginxで設定してみました。
<アプリケーション名>.<任意のドメイン> → <アプリケーション名>.empire
user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; keepalive_timeout 120; gzip on; server { server_name ~^(?<appname>[^\.]+)\..+$; location / { resolver 169.254.169.253 valid=2s; proxy_pass http://$appname.empire; } } }
上記nginx.confを仕込んだDockerイメージをDocker Hubでtakipone/empire-routerとして公開しているので、EC2/ECS環境でPullして試してみてください!ステートレスな構成なので、このコンテナをECS ServiceでデプロイしてInternet FacingなELBと組み合わせることももちろんできますよ!
手元にあるドメインclassmethod.deで試してみます。ワイルドカードレコード(*.classmethod.de)をInternet FacingなELBまたはコンテナを実行するホストに向けます。
curlコマンドを実行してみると。。。
$ curl acme-inc.classmethod.de Ok $
レスポンスが返ってきました!
まとめ
チュートリアルベースでしたが、Empireの一通りのセットアップと動作確認方法をご紹介してみました。未実装のサブコメントがあったり、まだまだ荒削りな感もありますが、肝心要のサービス部分はAWSのマネージドサービスですので、安心して使える、ある程度のデバッグ方法がわかるという点で他のコンテナ管理サービスと差別化できると思います。今後の機能強化に期待しつつ、引き続きウォッチしていきたいと思います。